package com.blue.integration.spring.redis;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;

/**
 * Set缓存
 *
 * @author zhengjin
 * @since 1.0 2018年06月24日
 */
public abstract class AbstractSetCache<T>
{
	//private static Logger logger = LoggerFactory.getLogger(AbstractSetCache.class);
	private static final String KEYS = "~keys";

	@Autowired
	protected RedisCache redisCache;

	protected Class<T> cacheClazz;


	@SuppressWarnings("unchecked")
	public AbstractSetCache()
	{
		Type[] types = ((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments();
		cacheClazz = (Class<T>) types[0];
	}

	/**
	 * 从缓存中获取对象列表
	 *
	 * @param id 缓存标识符
	 * @return 对象列表
	 */
	@SuppressWarnings("unchecked")
	public Set<T> list(Object id)
	{
		BoundSetOperations<String, Object> setOps = this.getSetOps(id);
		Set<Object> objectSet = setOps.members();
		Set<T> stringSet = new HashSet<>();
		objectSet.forEach(e -> stringSet.add((T)e));
		return stringSet;
	}

	/**
	 * 判断缓存中是否包含对象
	 *
	 * @param id 缓存标识符
	 * @param value 待判断的对象
	 * @return 包含返回 true，不包含返回 false
	 */
	public boolean contains(Object id, T value)
	{
		BoundSetOperations<String, Object> setOps = this.getSetOps(id);
		boolean exist = setOps.isMember(value);
		return exist;
	}

	/**
	 * 判断缓存中是否包含对象
	 *
	 * @param idList 缓存标识符列表
	 * @param value 待判断的对象
	 * @return 包含返回 true，不包含返回 false
	 */
	public boolean containsList(List<?> idList, T value)
	{
		if (idList == null || idList.isEmpty())
			return false;

		for (Object id : idList)
		{
			BoundSetOperations<String, Object> setOps = this.getSetOps(id);
			boolean exist = setOps.isMember(value);
			if (exist)
				return true;
		}
		return false;
	}

	/**
	 * 删除缓存
	 *
	 * @param id 缓存标识符
	 */
	public final void delete(Object id)
	{
		if (id == null)
		{
			Set<Object> keys = this.listKey();
			for (Object k : keys)
			{
				String key = this.getKey(k);
				redisCache.delete(key);
			}
			this.clearKey();
		}
		else
		{
			String key = this.getKey(id);
			redisCache.delete(key);
			this.deleteKey(id);
		}
	}

	/**
	 * 在缓存中添加Key
	 *
	 * @param ids 缓存识标符，不定参数
	 */
	protected final void addKey(Object...ids)
	{
		String key = this.getKey(KEYS);
		BoundSetOperations<String, Object> setOps = redisCache.setOps(key);
		setOps.add(ids);
	}

	/**
	 * 在缓存中删除Key
	 *
	 * @param ids 缓存识标符，不定参数
	 */
	protected final void deleteKey(Object...ids)
	{
		String key = this.getKey(KEYS);
		BoundSetOperations<String, Object> setOps = redisCache.setOps(key);
		setOps.remove(ids);
	}

	/**
	 * 清除缓存中的所有Key
	 */
	protected final void clearKey()
	{
		String key = this.getKey(KEYS);
		redisCache.delete(key);
	}

	/**
	 * 在缓存中列出所有Key
	 *
	 * @return Key列表
	 */
	protected final Set<Object> listKey()
	{
		String key = this.getKey(KEYS);
		BoundSetOperations<String, Object> setOps = redisCache.setOps(key);
		return setOps.members();
	}

	/**
	 * 获取Set操作
	 *
	 * @return Set操作
	 */
	protected final BoundSetOperations<String, Object> getSetOps(Object id)
	{
		this.initCache(id);
		String key = this.getKey(id);
		return redisCache.setOps(key);
	}

	/**
	 * 初始化缓存
	 */
	protected final void initCache(Object id)
	{
		String key = this.getKey(id);
		if (!redisCache.exist(key))
		{
			redisCache.lock(key);
			try
			{
				if (!redisCache.exist(key))
				{
					BoundSetOperations<String, Object> setOps = redisCache.setOps(key);
					this.initCacheItem(setOps, id);
					this.addKey(id);
					redisCache.expire(key, this.getExpireHour(), TimeUnit.HOURS);
				}
			}
			finally
			{
				redisCache.unlock(key);
			}
		}
	}

	/**
	 * 返回过期时间，默认1小时
	 */
	protected int getExpireHour()
	{
		return 1;
	}

	/**
	 * 初始化缓存数据
	 */
	protected abstract void initCacheItem(BoundSetOperations<String, Object> setOps, Object id);

	/**
	 * 获取键
	 */
	protected abstract String getKey(Object id);

}
